﻿using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UIElements;
using winS.UnityEditor.UIElement;
using Label = winS.UnityEditor.UIElement.Label;

namespace winS.UnityEditor.ProcessEditor
{
    /// <summary>
    /// 表示一个端口
    /// </summary>
    public abstract class Port : VisualElement
    {
        public event Action<Port> onConnected;
        public event Action<Port> onDisconnected;

        /// <summary>
        /// 端口是否被启用
        /// </summary>
        public bool isEnable { get; private set; }
        /// <summary>
        /// 端口是否已经连接
        /// </summary>
        public bool isConnected { get; private set; }

        public Color color { get; private set; } = new Color32(132, 228, 231, 255);

        public abstract PortType portType { get; }
        public int connectedCount => connectedNodeList.Count;
        public IEnumerable<StepNode> connectedNodes => connectedNodeList;
        public IEnumerable<Connectionline> connectionlines => connectionlineList;

        public Label label { get; private set; }
        public StepNode node { get; private set; }
        public Connector connector { get; private set; }

        protected readonly Linker linker;
        protected readonly StepGraphView stepGraphView;
        protected readonly List<Port> connectedPortList = new List<Port>();
        protected readonly List<StepNode> connectedNodeList = new List<StepNode>();
        protected readonly List<Connectionline> connectionlineList = new List<Connectionline>();

        public Port(StepNode stepNode)
        {
            node = stepNode;
            stepGraphView = stepNode.stepGraphView;
            style.flexDirection = FlexDirection.Row;
            style.alignItems = Align.Center;

            Add(connector = new Connector());
            Add(label = EditorElementUtility.elementFactory.CreateLabel());
            linker = new Linker(this, stepGraphView);
            connector.RegisterCallback<MouseEnterEvent>(MouseEnterCallback);
            connector.RegisterCallback<MouseLeaveEvent>(MouseLeaveCallback);
            Enable();
        }

        public void Enable()
        {
            isEnable = true;
            connector.ShowBox();
        }
        public void Disable()
        {
            isEnable = false;
            connector.HideBox();
        }
        public bool IsConnectTo(Port port)
        {
            return connectedPortList.Any(connectedPort => connectedPort == port);
        }
        public Vector2 GetConnectorWorldPoint()
        {
            Rect connectorLayout = connector.layout;
            connectorLayout.x = connectorLayout.y = 0f;
            return connector.LocalToWorld(connectorLayout.center);
        }

        public virtual void Connect(Connectionline connectionline)
        {
            isConnected = true;
            connector.ShowCap();
            connectionlineList.Add(connectionline);
            if (stepGraphView.canSendConnectEvent) onConnected?.Invoke(connectionline.inputPort == this ? connectionline.outputPort : connectionline.inputPort);
        }
        public virtual void Disconnect(Connectionline connectionline)
        {
            if (connectedPortList.Count == 0)
            {
                isConnected = false;
                connector.HideCap();
            }
            if (!connectionlineList.Remove(connectionline)) throw new Exception("连接线断开异常");
            if (stepGraphView.canSendConnectEvent) onDisconnected?.Invoke(connectionline.inputPort == this ? connectionline.outputPort : connectionline.inputPort);
        }

        private void MouseLeaveCallback(MouseLeaveEvent mouseLeaveEvent)
        {
            if (!isEnable) return;
            if (isConnected) return;
            if (linker.isLinking) return;
            connector.HideCap();
        }
        private void MouseEnterCallback(MouseEnterEvent mouseEnterEvent)
        {
            if (!isEnable) return;
            if (isConnected) return;
            if (linker.isLinking) return;
            connector.ShowCap();
        }

        public static implicit operator bool(Port self)
        {
            return self != null;
        }
    }
}